#-------------------------------------------
# Author:likezhang
# Email: likzhang@gmail.com
# Date: 12/13/2013
# All right reserved
#-------------------------------------------

class Model():
    def __init__(self, _dict): 
        # The id field hold id object
        if '_id' in _dict.keys():
            self._id = _dict['_id']
        #else:
        #    self._id = None
        
        
from db.mongodb import DBConn
from cfg import cfg
from datetime import datetime

from bson.dbref import DBRef

db_conn = DBConn()
db = db_conn.get_database(cfg.database)

class Dao():
    """ Data Access class
    """
    def __init__(self, ModelClass):
        self.db = db
        self.ModelClass = ModelClass
        self.ref_collections = None

    def get_time_string(self, timestamp):
        s = datetime.fromtimestamp(timestamp)
        return s.strftime('%Y-%m-%d %H:%M:%S')
 
    def save(self, model):
        """Updates an existing document or inserts a new document, depending on its document parameter.
        Arguments:
            model --- Can be Model type or dict type, if Model type if must be with id and the associated field must be Model too 
                        if model is a dict type, the associated field must be DBRef type
            associates --- The dict type, example {'item':'items'}
        Return:
            The '_id' value of to_save or [None] if manipulate is False and to_save has no _id field.
        """
        # The ID field must exist
        if isinstance(model, dict):
            """
            _dict = model
            for field in _dict:
                if isinstance(_dict[field], DBRef):
                    try:
                        _dict[field + '_ref'] = _dict.pop(field)
                    except KeyError:
                        pass
            """
            return self.db.save(self.collection, model)
        else:
            _dict = model.__dict__
            
            if self.ref_collections is not None:
                for field in self.ref_collections:
                    collection = self.ref_collections[field]
                    
                    try:
                        if field in _dict:
                            ref = _dict.pop(field)
                            _dict[field + '_ref'] = DBRef(collection, ref['_id'])
                    except KeyError:
                        pass
                    
            return self.db.save(self.collection, _dict)
 
    def remove(self, model):
        """
        Arguments:
            model -- dict type or Model object type
        Return:
            A document (dict) describing the effect of the remove or None if write acknowledgement is disabled.
        """
        if isinstance(model, dict):
            return self.db.remove(self.collection, query={'_id':model['_id']})
        else:
            if model._id is not None:
                return self.db.remove(self.collection, query={'_id':model._id})
            else:
                return None
    
    def find_one(self, query):
        """
        Arguments:
            query --- The dict type
        Return:
            Raises TypeError if any of the arguments are of improper type. 
            Returns an instance of Cursor corresponding to this query.
        """
        _dict = self.db.find_one(self.collection, query)

        
        if _dict is not None:
            ret = {}
            for field in _dict:
                if isinstance(_dict[field], DBRef):
                    _dbref = _dict[field]
                    field = field[:-4] #remove '_ref'
                    _collection = _dbref.collection
                    ret[field] = self.db.find_one(_collection, {'_id':_dbref.id})
                else:
                    ret[field] = _dict[field]
            return self.ModelClass(ret)
        else:
            return None


    
    def find(self, query={}):
        """
        Arguments:
            query --- The dict type
        Return:
            Raises TypeError if any of the arguments are of improper type. 
            Return array of the ModelClass.
        """
        cursor = self.db.find(self.collection, query=query)
        
        models = []
        for _dict in cursor:
            if _dict is not None:
                ret = {}
                for field in _dict:
                    if isinstance(_dict[field], DBRef):
                        _dbref = _dict[field]
                        field = field[:-4] #remove '_ref'
                        _collection = self.ref_collections[field]
                        ret[field] = self.db.find_one(_collection, {'_id':_dbref.id})
                    else:
                        ret[field] = _dict[field]
                models.append( self.ModelClass(ret) )
        
        return models
    
    
    def all(self, order=None):
        return self.find()
               
    """
        self.ModelClass = ModelClass
    
        
    def delete(self, *args, **kwargs):
        ''' filter and get the first
        Arguments:
            kwargs -- dict type
        '''    
        self.session.query(self.ModelClass).filter_by(**kwargs).delete()
        self.session.flush()
    
    def add_to_db(self, model):
        '''
        Arguments:
            model --- object of the model class
        '''
        self.session.add(model)
        self.session.commit()
    """
    '''    
    def remove_from_db(self, model):
        self.session.delete(model)
        self.session.commit()

    
    def update_category(self, _id, name, description):
        _category = self.session.query(Category).get(_id)
        _category.name = name
        _category.description = description
        
        self.session.commit()
    '''
    """
    The following are for the mysql and sqlalchemy 
    def all(self, order_by=None):
        '''Get all rows
        '''
        if order_by is None:
            return self.session.query(self.ModelClass).all()
        else:
            return self.session.query(self.ModelClass).order_by(order_by)
    
    def filter(self, *criterion):
        '''
        Arguments:
            criterion -- list
        '''
        return self.session.query(self.ModelClass).filter(criterion).first()

    
    def filter_by(self, *args, **kwargs):
        '''
        Arguments:
            kwargs -- dict type
        '''    
        return self.session.query(self.ModelClass).filter_by(**kwargs)
    
    
    def filter_first(self, *args, **kwargs):
        ''' filter and get the first
        Arguments:
            kwargs -- dict type
        '''    
        return self.session.query(self.ModelClass).filter_by(**kwargs).first()

    """